import { useMemoizedFn } from 'ahooks';
import type { SelectProps } from 'antd';
import { Select, Spin } from 'antd';

import { debounce } from 'lodash';
import { useEffect, useMemo, useState } from 'react';

export interface DefaultOptionType {
  value: number;
  label: string | React.ReactNode;
}

export interface DebounceSelectProps<OptionType extends DefaultOptionType>
  extends Omit<SelectProps<any, DefaultOptionType>, 'options' | 'children'> {
  fetchOptions: (search: string) => Promise<OptionType[]>;
  debounceTimeout?: number;
}

function DebounceSelect<
  OptionType extends DefaultOptionType = DefaultOptionType,
>({
  fetchOptions,
  debounceTimeout = 800,
  ...props
}: DebounceSelectProps<OptionType>) {
  const [fetching, setFetching] = useState(false);
  const [options, setOptions] = useState<OptionType[]>([]);

  const fetchOptionsEvent = useMemoizedFn(fetchOptions);

  const debounceFetcher = useMemo(() => {
    const loadOptions = (value: string) => {
      setOptions([]);
      setFetching(true);

      fetchOptionsEvent(value).then((newOptions) => {
        setOptions(newOptions);
        setFetching(false);
      });
    };

    return debounce(loadOptions, debounceTimeout);
  }, [fetchOptionsEvent, debounceTimeout]);

  useEffect(() => {
    if (!options.length) {
      debounceFetcher('');
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [debounceFetcher, props.value]);

  return (
    <Select
      filterOption={false}
      onSearch={debounceFetcher}
      notFoundContent={fetching ? <Spin size="small" /> : null}
      showSearch
      allowClear
      {...props}
      options={options}
    />
  );
}

export default DebounceSelect;
